Skip to content

Enforce canonical dispatch-repository safe-output key (deprecate underscore alias)#42150

Merged
pelikhan merged 8 commits into
mainfrom
copilot/doc-healer-fix-dash-alias
Jun 29, 2026
Merged

Enforce canonical dispatch-repository safe-output key (deprecate underscore alias)#42150
pelikhan merged 8 commits into
mainfrom
copilot/doc-healer-fix-dash-alias

Conversation

Copilot AI commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

dispatch-repository is now the canonical safe-output key, matching the dashed naming convention used by all other safe-output types (dispatch-workflow, call-workflow, merge-pull-request, etc.). dispatch_repository (underscore) remains supported as a deprecated runtime alias and schema alias during the migration window.

  • Compiler behavior

    • safe-outputs.dispatch-repository (dashed) is now the canonical key, checked first.
    • safe-outputs.dispatch_repository (underscore) remains a backward-compatible alias; a deprecation warning is logged when it is used.
  • Codemod

    • gh aw fix includes a new safe-output-dispatch-repository-key codemod that migrates safe-outputs.dispatch_repositorysafe-outputs.dispatch-repository automatically.
    • The codemod logs a warning and skips migration when both keys are present in the same workflow file.
  • Workflow parsing coverage

    • Added precedence test: dashed form wins when both keys coexist in the same map.
    • Underscore alias continues to be accepted (backward compat test retained).
  • Schema

    • dispatch-repository is the primary schema property.
    • dispatch_repository is kept as a $ref alias for backward compatibility.
// before: underscore-only
if configData, exists = outputMap["dispatch_repository"]; !exists {
    return nil
}

// after: dashed canonical with underscore alias
if configData, exists = outputMap["dispatch-repository"]; !exists {
    if configData, exists = outputMap["dispatch_repository"]; !exists {
        return nil
    }
    // underscore form is deprecated; signal migration
    dispatchRepositoryLog.Print("WARNING: safe-outputs.dispatch_repository is deprecated; rename to dispatch-repository or run `gh aw fix`")
}

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix dash alias in documentation for safe-outputs Enforce canonical dispatch_repository safe-output key (remove dashed alias) Jun 29, 2026
Copilot AI requested a review from pelikhan June 29, 2026 00:25
@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §28342769269

Field Value
Category bug
Risk low
Score 40/100
Priority medium
Action defer
Status Draft — awaiting promotion to ready

Score breakdown: Impact 20 · Urgency 8 · Quality 12

Rationale: Tiny targeted fix removing undocumented dashed alias from dispatch_repository parsing (+22/−9, 3 files). Enforces contract alignment between runtime and schema. Tests added. No CI yet. Currently draft — defer until promoted to ready.

i️ Promote to ready when complete and CI passes.

Generated by 🔧 PR Triage Agent · 107.8 AIC · ⌖ 10.9 AIC · ⊞ 5.4K ·

@github-actions

Copy link
Copy Markdown
Contributor

Great work on this one! 🎉 The dispatch_repository schema-alignment fix is clean, well-scoped, and easy to reason about.

  • Runtime change in dispatch_repository.go is minimal (1 addition, 5 deletions — surgical removal of the dashed fallback).
  • Test coverage is solid: existing parser tests updated to assert rejection of the dashed key, plus a new schema-level regression guard added in schema_location_test.go.
  • Description clearly explains the contract drift, the fix, and includes a readable before/after snippet.

This PR looks ready for review. 🟢

Generated by ✅ Contribution Check · 277.8 AIC · ⌖ 16.5 AIC · ⊞ 6K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot align using dashes like other safe outputs

Copilot AI and others added 3 commits June 29, 2026 02:22
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@copilot align using dashes like other safe outputs

Implemented in e35808a (plus follow-ups f706b8f and 453e85c). dispatch-repository is now the canonical safe-output key (aligned with dashed naming), while dispatch_repository remains a backward-compatible alias.

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot create a codemod to rename to the proper syntax

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@copilot create a codemod to rename to the proper syntax

Implemented in b18f3a7. Added a gh aw fix codemod that renames safe-outputs.dispatch_repository to safe-outputs.dispatch-repository, registered it in the codemod registry, and added codemod/registry tests.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §28357644191

Field Value
Category bug
Risk 🟢 Low
Score 36/100 (Impact 20 · Urgency 8 · Quality 8)
Action defer

Carried over — 7.9h old. Removes undocumented dashed alias for dispatch_repository (+22/-9, 3 files). Schema alignment fix. Draft, no CI yet. Defer until promoted to ready.

Generated by 🔧 PR Triage Agent · 89.9 AIC · ⌖ 12 AIC · ⊞ 5.4K ·

@pelikhan pelikhan marked this pull request as ready for review June 29, 2026 09:11
Copilot AI review requested due to automatic review settings June 29, 2026 09:11
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the safe-outputs key name for the repository-dispatch safe output, shifting the canonical key from dispatch_repository (underscore) to dispatch-repository (dash) while retaining underscore as a deprecated alias (plus a codemod to migrate).

Changes:

  • Make dispatch-repository the canonical runtime parse key (with dispatch_repository as a fallback alias).
  • Update schema + schema validation tests to define/accept dispatch-repository and mark dispatch_repository as a deprecated alias.
  • Add a gh aw fix codemod to rewrite safe-outputs.dispatch_repositorysafe-outputs.dispatch-repository, and update docs/tests/warnings accordingly.
Show a summary per file
File Description
pkg/workflow/dispatch_repository.go Switches runtime parsing to prefer dispatch-repository (underscore fallback).
pkg/workflow/dispatch_repository_test.go Updates unit tests to use dispatch-repository as primary and keep underscore as alias coverage.
pkg/workflow/dispatch_repository_experimental_warning_test.go Updates expected experimental warning text to dispatch-repository.
pkg/workflow/compiler_validators.go Updates emitted experimental warning string to dispatch-repository.
pkg/parser/schemas/main_workflow_schema.json Makes dispatch-repository canonical in schema and adds dispatch_repository as a deprecated alias.
pkg/parser/schema_test.go Updates schema path assertions to reference dispatch-repository.
pkg/parser/schema_location_test.go Adds coverage that dispatch-repository is accepted by schema validation.
pkg/cli/fix_codemods.go Registers the new dispatch-repository key migration codemod.
pkg/cli/fix_codemods_test.go Updates expected codemod list/order to include the new codemod.
pkg/cli/codemod_safe_output_dispatch_repository_key.go Implements migration from underscore key to dashed key in frontmatter text.
pkg/cli/codemod_safe_output_dispatch_repository_key_test.go Adds unit tests validating the codemod behavior (rename, indentation/comments, no-ops).
docs/src/content/docs/reference/safe-outputs.md Updates docs to describe/configure dispatch-repository and the warning string.
docs/src/content/docs/reference/glossary.md Updates glossary entry and reference links to dispatch-repository.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comments suppressed due to low confidence (1)

pkg/workflow/dispatch_repository.go:45

  • The parser now treats dispatch-repository as canonical, but the nearby debug log messages still refer to dispatch_repository, which can make troubleshooting misleading. Update these log strings to match the canonical key name (and ideally keep the terminology consistent throughout the function).
	}

	configMap, ok := configData.(map[string]any)
	if !ok {
		dispatchRepositoryLog.Print("dispatch_repository value is not a map, skipping")
  • Files reviewed: 13/13 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines +36 to 40
// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {
if configData, exists = outputMap["dispatch_repository"]; !exists {
return nil
}
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

@github-actions

Copy link
Copy Markdown
Contributor

Skills-Based Review Summary 🧠

Applied /zoom-out, /tdd, /diagnose, and /improve-codebase-architecture against PR #42150.

Verdict: REQUEST_CHANGES — two substantive issues plus minor suggestions.

🔴 Blocking issues (2)
  1. PR title/description contradict the implementation. The title says "Enforce canonical dispatch_repository" (underscore) and the description includes an "after: underscore-only" snippet — but the code makes dispatch-repository (dashed) canonical. The implementation is correct and consistent with every other safe-output key (dispatch-workflow, call-workflow, etc.). The title just needs to be updated to reflect what was actually done.

  2. Missing test for key-precedence behaviour. The parser now checks the dashed key before the underscore fallback. If both keys exist in one map, the dashed value wins — but this is the core behaviour change of the PR and there is currently no test for it. See the inline comment on dispatch_repository_test.go for a concrete test skeleton.

🟡 Suggestions (4)
  • dispatch_repository.go — Emit a deprecation warning (log line or stderr) when the underscore fallback path is taken, so users know to run gh aw fix.
  • codemod_safe_output_dispatch_repository_key_test.go — Add a no-op test where dispatch_repository appears only in a YAML value (not as a key), to guard against false-positive renames.
  • schema_location_test.go — Fix the PR description: it says the new test ensures dashed key fails validation, but wantErr: false means the opposite. The test is correct; the description is not.
  • main_workflow_schema.json — Consider "deprecated": true on the alias property for IDE tooling (low priority).

@copilot please address the review comments above.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 96.8 AIC · ⌖ 8.35 AIC · ⊞ 6.6K ·
Comment /matt to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skills-Based Review 🧠

Applied /zoom-out, /tdd, /diagnose, and /improve-codebase-architecture — requesting changes primarily on the PR title/description accuracy and a missing precedence test.

📋 Key Themes & Highlights

Key Themes

  • PR title/description contradict the implementation: The title says "Enforce canonical dispatch_repository" (underscore) and the description includes an "after: underscore-only" snippet, but the implementation makes the dashed form canonical (dispatch-repository checked first) and deprecates underscore. The code is correct and consistent with all other safe-output keys (which all use dashes), but the misleading title will cause confusion for reviewers and future maintainers.
  • Missing precedence test: The parser now checks dashed before underscore, but no test exercises the case where both keys coexist in the same map. This is the core behaviour change and should be regression-tested.
  • Silent deprecation: Users with dispatch_repository in existing workflows get no compiler warning; they must know to run gh aw fix independently.

Positive Highlights

  • ✅ Codemod is well-structured: correct no-op guards, indentation/comment preservation, and four meaningful test cases
  • ✅ Schema change correctly places the dashed form as the primary property and the underscore as a $ref alias — matches project conventions
  • ✅ Documentation and anchor links updated consistently throughout
  • ✅ Experimental warning message updated in sync with the key rename
  • TestParseDispatchRepositoryConfig_UnderscoreAlias correctly documents the backward-compat contract

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 96.8 AIC · ⌖ 8.35 AIC · ⊞ 6.6K
Comment /matt to run again

if configData, exists = outputMap["dispatch_repository"]; !exists {
if configData, exists = outputMap["dispatch-repository"]; !exists {
// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/zoom-out] The implementation makes dispatch-repository (dashed) canonical, but the PR title says "Enforce canonical dispatch_repository safe-output key (remove dashed alias)" — the opposite direction. The code is correct (all other safe-output keys use dashes: dispatch-workflow, call-workflow, merge-pull-request), but the title and PR description "after: underscore-only" snippet are misleading and could confuse future maintainers.

💡 Suggested title

Consider retitling to something like:

Enforce canonical dispatch-repository safe-output key (deprecate underscore alias)

And update the PR description code snippet to match the actual implementation, which still accepts both forms (dashed preferred).

@copilot please address this.

config := compiler.parseDispatchRepositoryConfig(outputMap)
require.NotNil(t, config, "Config should be parsed from dash form")
require.NotNil(t, config, "Config should be parsed from underscore alias")
require.Len(t, config.Tools, 1, "Should have 1 tool")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The renamed test verifies the underscore alias still works, but the new precedence (dashed checked first) is not tested. When both dispatch-repository and dispatch_repository exist in the same map, the dashed value should win — but there is no test for this.

💡 Suggested test
func TestParseDispatchRepositoryConfig_DashPrecedenceOverUnderscore(t *testing.T) {
	compiler := NewCompiler(WithVersion("1.0.0"))

	outputMap := map[string]any{
		"dispatch-repository": map[string]any{
			"dash_tool": map[string]any{
				"workflow":   "dash.yml",
				"event_type": "dash_event",
				"repository": "github/canonical",
			},
		},
		"dispatch_repository": map[string]any{
			"underscore_tool": map[string]any{
				"workflow":   "underscore.yml",
				"event_type": "underscore_event",
				"repository": "github/alias",
			},
		},
	}

	config := compiler.parseDispatchRepositoryConfig(outputMap)
	require.NotNil(t, config)
	_, hasDashTool := config.Tools["dash_tool"]
	assert.True(t, hasDashTool, "dashed form should take precedence")
	_, hasUnderscoreTool := config.Tools["underscore_tool"]
	assert.False(t, hasUnderscoreTool, "underscore form should be shadowed by dashed form")
}

@copilot please address this.

},
},
filePath: "/test/workflow.md",
wantErr: false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/zoom-out] The test correctly uses wantErr: false (the dashed key should be accepted), but the PR description says "Added parser validation coverage to ensure safe-outputs.dispatch-repository fails schema/location validation." — the opposite of what this test asserts. Either the test or the PR description is wrong; based on the schema change (dashed form is now canonical), the test is correct.

Please update the PR description to accurately reflect the intent of this test case.

@copilot please address this.

if configData, exists = outputMap["dispatch-repository"]; !exists {
// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {
if configData, exists = outputMap["dispatch_repository"]; !exists {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/diagnose] When the underscore alias is used as the fallback path (no dispatch-repository key found), the compiler silently accepts it with no deprecation signal to the user. Users who have not yet migrated their workflows will have no indication that they need to run gh aw fix — unless they happen to check the changelog.

💡 Suggested improvement

Consider emitting a deprecation warning when the underscore fallback is taken:

if configData, exists = outputMap["dispatch-repository"]; !exists {
    if configData, exists = outputMap["dispatch_repository"]; !exists {
        return nil
    }
    // underscore form is deprecated; signal migration
    dispatchRepositoryLog.Print("WARNING: safe-outputs.dispatch_repository is deprecated; rename to dispatch-repository or run ghawfix")
}

This gives users actionable guidance without being a hard error.

@copilot please address this.

"dispatch_repository": {
"$ref": "#/properties/safe-outputs/properties/dispatch-repository",
"description": "Deprecated alias for dispatch-repository."
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/improve-codebase-architecture] The deprecated alias has a human-readable "description": "Deprecated alias for dispatch-repository." which is good, but JSON Schema draft 2019-09 and later support the "deprecated": true annotation for IDE/tooling signalling. Other deprecated aliases in this codebase do not use it either, but if tooling like Intellij or VS Code JSON language server honours it, users would get inline deprecation hints.

💡 Optional improvement
"dispatch_repository": {
  "$ref": "#/properties/safe-outputs/properties/dispatch-repository",
  "description": "Deprecated alias for dispatch-repository.",
  "deprecated": true
},

Low priority — only consider if your JSON schema tooling chain supports it.

@copilot please address this.

assert.False(t, applied)
assert.Equal(t, content, result)
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[/tdd] The four test cases are a solid set, but there is a gap: the codemod skips migration when safe-outputs is absent from the frontmatter (handled by the early return), but there is no test for a file that has safe-outputs but dispatch_repository is not a direct child — e.g. it appears nested deeper or as a plain text value inside a description field. A false-positive rename in YAML value content would be a silent data corruption.

💡 Suggested additional test
t.Run("no-op when dispatch_repository appears only in a description value", func(t *testing.T) {
    content := `---
safe-outputs:
  some-tool:
    description: "Triggers via dispatch_repository: mechanism"
    workflow: router.yml
    event_type: dispatch
    repository: github/gh-aw
---
`
    frontmatter := map[string]any{
        "safe-outputs": map[string]any{
            "some-tool": map[string]any{
                "description": "Triggers via dispatch_repository: mechanism",
            },
        },
    }
    result, applied, err := codemod.Apply(content, frontmatter)
    require.NoError(t, err)
    assert.False(t, applied)
    assert.Equal(t, content, result)
})

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 90/100 — Excellent

Analyzed 10 tests (5 new sub-tests + 5 modified): 9 design, 1 implementation, 0 hard guideline violations; soft warning for missing assertion messages.

📊 Metrics & Test Classification (10 tests analyzed)
Metric Value
New/modified tests analyzed 10
✅ Design tests (behavioral contracts) 9 (90%)
⚠️ Implementation tests (low value) 1 (10%)
Tests with error/edge cases 8 (80%)
Duplicate test clusters 0
Test inflation detected No (new file ratio 1.56:1)
🚨 Coding-guideline violations 0 (no mock libraries; build tags present)
Test File Classification Issues Detected
TestSafeOutputDispatchRepositoryKeyCodemod/"metadata" pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:13 ⚠️ Implementation Tests struct field values only; no error coverage; all 5 assertions lack descriptive messages
TestSafeOutputDispatchRepositoryKeyCodemod/"renames safe-outputs dispatch_repository key" pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:23 ✅ Design Missing messages on require.NoError, assert.True
TestSafeOutputDispatchRepositoryKeyCodemod/"preserves comments and indentation" pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:57 ✅ Design Missing messages on require.NoError, assert.True
TestSafeOutputDispatchRepositoryKeyCodemod/"no-op when deprecated key absent" pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:86 ✅ Design Missing messages on require.NoError, assert.False, assert.Equal
TestSafeOutputDispatchRepositoryKeyCodemod/"no-op when both keys already exist" pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:115 ✅ Design Missing messages on require.NoError, assert.False, assert.Equal
TestGetAllCodemods_ContainsExpectedCodemods pkg/cli/fix_codemods_test.go:85 ✅ Design
TestValidateMainWorkflowFrontmatterWithSchemaAndLocation_AdditionalProperties pkg/parser/schema_location_test.go:322 ✅ Design
TestMainWorkflowSchema_WorkflowCallAndDispatchInputDefsDisallowUnknownKeys pkg/parser/schema_test.go:957 ✅ Design
TestDispatchRepositoryExperimentalWarning pkg/workflow/dispatch_repository_experimental_warning_test.go:107 ✅ Design
TestParseDispatchRepositoryConfig_UnderscoreAlias pkg/workflow/dispatch_repository_test.go:94 ✅ Design

Go: 10 (*_test.go); JavaScript: 0.

⚠️ Flagged Tests — Soft Warnings (1 concern)

TestSafeOutputDispatchRepositoryKeyCodemod/"metadata" (pkg/cli/codemod_safe_output_dispatch_repository_key_test.go:13) — ⚠️ Implementation: verifies codemod struct field values (ID, Name, Description, IntroducedIn) rather than any observable behavioral contract. Fragile — breaks on rename refactoring without hiding a behavioral regression. All 5 assertions also lack descriptive message arguments.

Soft: missing assertion messages — 12 of 20 assertions in the new test file lack a descriptive message argument. Project guidelines require a context string on every testify assertion call:

  • assert.True(t, applied)assert.True(t, applied, "codemod should have applied the rename")
  • assert.False(t, applied)assert.False(t, applied, "codemod should be a no-op when key is absent")
  • assert.Equal(t, content, result)assert.Equal(t, content, result, "content should be unchanged when codemod is a no-op")

These are soft warnings and do not block the PR.

Verdict

Check passed. 10% implementation tests (threshold: 30%). The new codemod test covers the rename path, comment preservation, and two idempotency edge cases — all meaningful behavioral contracts. The behavioral reversal in TestParseDispatchRepositoryConfig_UnderscoreAlias (formerly DashAliasRejected) correctly documents the new backward-compatibility guarantee.

References: §28361230541

🧪 Test quality analysis by Test Quality Sentinel · 115.6 AIC · ⌖ 18.1 AIC · ⊞ 8.1K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Test Quality Sentinel: 90/100. Test quality is excellent — 10% of new tests are implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUEST_CHANGES — Two blocking issues plus one medium concern.

🔴 Blocking: PR title and description are inverted relative to the implementation

The PR commits went through a mid-flight reversal (commit e35808a6a flipped underscore-first → dashed-first) but the title and description were never updated. The title still reads "remove dashed alias" while the code makes dispatch-repository the canonical key. Anyone using this PR as a reference for the change's intent — in blame, changelogs, or security audits — will reach the wrong conclusion. Please update the title/description to accurately state that dashed form is now canonical and underscore is the deprecated alias.

🟡 Blocking: Seven log messages in `dispatch_repository.go` still use the old `dispatch_repository` identifier

The entry log was updated, but the error paths and generateDispatchRepositoryTool were not touched. The result is a parse session that starts with "Parsing dispatch-repository configuration" and then reports "dispatch_repository value is not a map, skipping" — confusing for any operator who reads the logs. These belong in this PR since it is the change that made the naming inconsistent.

🟡 Non-blocking: silent skip when both keys coexist in a workflow

The codemod correctly avoids clobbering configs that have both dispatch_repository and dispatch-repository, but it emits no signal — not even a debug log — to tell the user their file was skipped and why. Consider adding a log line so gh aw fix is transparent about what it skipped.

🔎 Code quality review by PR Code Quality Reviewer · 102.9 AIC · ⌖ 7.45 AIC · ⊞ 5.2K
Comment /review to run again

if configData, exists = outputMap["dispatch_repository"]; !exists {
if configData, exists = outputMap["dispatch-repository"]; !exists {
// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description is the inverse of the actual implementation: The PR title says "remove dashed alias" and the body shows dispatch_repository as the only accepted key after the change — but this code makes dispatch-repository (dashed) canonical and keeps dispatch_repository (underscore) as the backward-compatible fallback.

💡 Details and impact

The PR description reads:

// after: underscore-only
if configData, exists = outputMap["dispatch_repository"]; !exists {
    return nil
}

But the actual code shipped here is:

// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {
    if configData, exists = outputMap["dispatch_repository"]; !exists {
        return nil
    }
}

The implementation was reversed mid-PR (commit e35808a6a flipped from underscore-first to dashed-first), but the PR title and description were never updated. Any future maintainer reading "remove dashed alias" will confidently (and incorrectly) conclude that dispatch-repository is no longer accepted — when in fact it is now the primary key. The PR description must be corrected before merge.

if configData, exists = outputMap["dispatch_repository"]; !exists {
if configData, exists = outputMap["dispatch-repository"]; !exists {
// dispatch-repository is canonical; keep underscore form as a backward-compatible alias.
if configData, exists = outputMap["dispatch-repository"]; !exists {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale log messages throughout the function still use the underscore form: The entry log and comment were updated to dispatch-repository, but the rest of parseDispatchRepositoryConfig and all of generateDispatchRepositoryTool still log dispatch_repository.

💡 Affected lines
  • Line 45: "dispatch_repository value is not a map, skipping"
  • Line 49: "Parsing dispatch_repository tools map with %d entries"
  • Line 111: "Parsed dispatch_repository tool %q: workflow=..."
  • Line 118: "No valid tools found in dispatch_repository config"
  • Line 125: function comment "generates an MCP tool definition for a specific dispatch_repository tool"
  • Line 129: "Generating dispatch_repository tool: key=%s"
  • Line 168: "Generated dispatch_repository tool: name=%s, properties=%d"

When a user configures safe-outputs.dispatch-repository and hits an error, the log says dispatch_repository value is not a map — a key they didn't use. This makes operational debugging actively misleading.

}
_, hasOld := safeOutputsMap["dispatch_repository"]
_, hasNew := safeOutputsMap["dispatch-repository"]
return hasOld && !hasNew

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Silent no-op when both keys coexist — no user-facing signal: When a workflow has both dispatch_repository and dispatch-repository under safe-outputs, gh aw fix will silently do nothing, leaving the user with a deprecated key that the parser ignores (since dispatch-repository takes priority at runtime).

💡 Suggested fix

At minimum emit a log that the user can see via --log-level debug (or upgrade to a warning), so they know manual cleanup is needed:

_, hasOld := safeOutputsMap["dispatch_repository"]
_, hasNew := safeOutputsMap["dispatch-repository"]
if hasOld && hasNew {
    // Both keys present; skip migration but warn
    safeOutputDispatchRepositoryKeyCodemodLog.Print(
        "WARN: safe-outputs has both dispatch_repository and dispatch-repository; manual review needed")
    return false
}
return hasOld

Without this, gh aw fix is not idempotent in user-perceptible terms: running it on a dual-key file produces no output and no change, but the deprecated key persists.

…decision

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (273 new lines in pkg/) but does not have a linked Architecture Decision Record (ADR).

📄 Draft ADR committed: docs/adr/42150-make-dispatch-repository-canonical-safe-output-key.md — review and complete it before merging.

🔒 This PR cannot merge until an ADR is linked in the PR body.

📋 What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-42150: Make dispatch-repository the Canonical Safe-Output Key

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

❓ Why ADRs Matter

"AI made me procrastinate on key design decisions. Because refactoring was cheap, I could always say 'I'll deal with this later.' Deferring decisions corroded my ability to think clearly."

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

📋 Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 42150-make-dispatch-repository-canonical-safe-output-key.md for PR #42150).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 41.8 AIC · ⌖ 9.8 AIC · ⊞ 8.4K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

The code implementation is internally consistentdispatch-repository (dashed) is correctly established as canonical throughout the compiler, schema, warning messages, docs, and tests. The codemod correctly migrates the underscore form. The correctness logic is sound.

However, there are three items worth addressing before merge:

⚠️ Critical: PR title and description contradict the implementation

The PR title says "remove dashed alias" and the body says "making underscore-only keying canonical," but the code does the exact opposite — it promotes dispatch-repository (dashed) to canonical and demotes dispatch_repository (underscore) to a deprecated alias with a migration codemod. All other safe-output keys in the project already use dashes (dispatch-workflow, call-workflow, merge-pull-request, etc.), so the dashed form is the correct convention. The code is right; the description needs to be rewritten to reflect what actually landed.

Suggested corrected description:

  • dispatch-repository is now the canonical key (matching the dashed convention of all other safe-output types)
  • dispatch_repository (underscore) remains supported as a deprecated runtime alias and schema alias during the migration window
  • A codemod (safe-output-dispatch-repository-key) migrates existing workflow files from underscore to dashed automatically
  • Schema, compiler warning, docs, and tests are all updated to the dashed form

🔧 Non-blocking: Stale log messages in dispatch_repository.go

Several log messages inside the same file still reference dispatch_repository (underscore) on lines 45, 49, 111, 118, 125, 129, 164, 168 — see inline comment. Minor but breaks the consistency signal introduced by this PR.

🔧 Non-blocking: IntroducedIn: "1.0.0" version accuracy

See inline comment on the codemod file. Recent codemods use the specific release version for traceability.

Files reviewed
  • pkg/workflow/dispatch_repository.go — lookup order swap ✅
  • pkg/workflow/compiler_validators.go — warning message ✅
  • pkg/workflow/dispatch_repository_test.go — test direction ✅
  • pkg/workflow/dispatch_repository_experimental_warning_test.go — warning text ✅
  • pkg/parser/schemas/main_workflow_schema.json — schema rename + alias ✅ (see inline note)
  • pkg/parser/schema_location_test.go — positive acceptance test ✅
  • pkg/parser/schema_test.go — schema path update ✅
  • pkg/cli/codemod_safe_output_dispatch_repository_key.go — new codemod ✅ (see inline notes)
  • pkg/cli/codemod_safe_output_dispatch_repository_key_test.go — tests ✅
  • pkg/cli/fix_codemods.go — registration ✅
  • pkg/cli/fix_codemods_test.go — expected order ✅
  • docs/src/content/docs/reference/glossary.md — doc update ✅
  • docs/src/content/docs/reference/safe-outputs.md — doc update ✅

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 68.9 AIC · ⌖ 9.82 AIC · ⊞ 4.9K

Comments that could not be inline-anchored

pkg/workflow/dispatch_repository.go:45

These log messages still use the underscore form while the function comment and lookup order were updated to the dashed canonical form. Lines 45 and 49 (and further at 111, 118, 125, 129, 164, 168) should be updated for consistency:

// line 45 - stale
dispatchRepositoryLog.Print(&quot;dispatch_repository value is not a map, skipping&quot;)
// line 49 - stale
dispatchRepositoryLog.Printf(&quot;Parsing dispatch_repository tools map with %d entries&quot;, ...)

Should read dispatch-repository to match th…

pkg/cli/codemod_safe_output_dispatch_repository_key.go:16

The IntroducedIn version is &quot;1.0.0&quot; but newer codemods use the specific version in which the breaking change was made (e.g., &quot;1.0.44&quot;, &quot;1.0.47&quot;). Since dispatch-repository is being made canonical in this release (v1.0.65+), this field should reflect the actual release version rather than 1.0.0. Using 1.0.0 is not harmful (the codemod is idempotent), but it makes the provenance harder to trace in the audit trail.

@copilot please address this.

pkg/parser/schemas/main_workflow_schema.json:9295

The dispatch_repository schema property is kept as a $ref alias to dispatch-repository. This means the schema still validates workflows using the underscore form — which is the right call for backward compatibility while the codemod migrates users.

However, the PR description says "Added parser validation coverage to ensure safe-outputs.dispatch-repository fails schema/location validation" — that description is incorrect on two counts:

  1. The added test in schema_location_test.go

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@pelikhan pelikhan changed the title Enforce canonical dispatch_repository safe-output key (remove dashed alias) Enforce canonical dispatch-repository safe-output key (deprecate underscore alias) Jun 29, 2026
…mod improvements

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Addressed in the latest commit. Changes made:

  • Fixed all stale underscore log messages in dispatch_repository.godispatch-repository
  • Added deprecation warning when the underscore alias fallback is taken at runtime
  • Added TestParseDispatchRepositoryConfig_DashPrecedenceOverUnderscore test for both-keys-coexist precedence behaviour
  • Extracted safeOutputDispatchRepositoryKeyHasBothKeys in the codemod and emits a WARN log + skips migration transparently when both keys are present
  • Added codemod test for description-value false positive
  • Fixed IntroducedIn from "1.0.0" to "1.0.65"
  • Updated PR title and description to accurately reflect the implementation (dashed canonical, underscore deprecated)

@pelikhan pelikhan merged commit a4ebbca into main Jun 29, 2026
29 checks passed
@pelikhan pelikhan deleted the copilot/doc-healer-fix-dash-alias branch June 29, 2026 10:27
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants